/*   GFX2.C/H - copyright (c) Toni R„s„nen, 1998
 *   ------------------------------------------
 *   General graphics routines, somewhat changed to allow various modes.
 *   Files required: GFX2.C, GFX2.H, FLIP.ASM, SPRITE.ASM
 *
 *   Revision history:
 *       20.06.98: Added headers
 *
 * 'modeset' - variable: 1:linear, 0:banked
 * notes:   - use "setvesamode(int)" to set mode
 *          - remember to call "set_videoparams()"
 *

*/

#include <string.h>
#include <go32.h>
#include <conio.h>
#include <pc.h>
#include <stdio.h>
#include <stdlib.h>
#include <dpmi.h>
#include <assert.h>
#include <sys/nearptr.h>
#include "gfx2.h"
#include "mpack.h"

gfxPFV putnpic,putblendpic;

int scr_y_adxs[480];

int spr_ysize,spr_xsize,spr_xmaxadd; // required by putnpic -function

int vidmemsize;  // video memory size in bytes
int sxsize,sysize,bytexsize;
char bytesperpixel;

char gen_palette[768];   // Palette array
short drawcolor;
void *ruutu,*ruutu2;  // doublebuffering
void *zbuffer;        // zbuffer 8bit
short VBESelector;
char nocolor;
char modeset;



char *spritedest;
int currbank,banksize;

int xclipmax,yclipmax;
char force_bankedVESA; // cmdline swtch: force to banked (vesa 1.2) mode

struct VBE_vInfo vbeinfo;
struct VBE_mInfo modeinfo;

//unsigned char *videoptr = (unsigned char *)0xA0000;

// #define PPix(x, y, c) _farpokeb(VBESelector , y*640+x, c);

void vbe_setbank(int n)
{  __dpmi_regs r;
   r.x.ax = 0x4f05;
   r.x.bx = 0x0000;
   r.x.dx = n;
   currbank = n;
   __dpmi_int (0x10, &r);
}

extern void flip_banked(int offs, int size);
extern void VBE2flip (int size);

// *******************************************************************
void _bankedflip2()
{
   int l = vidmemsize, o = 0, p = 0, s;

   while (l > 0)
     {  if (l > banksize)  s = banksize; else s = l;
        vbe_setbank (p++);

        flip_banked(o, s>>2);

        l -= s;
        o += s;
     }
}


// *******************************************************************
void flip()
{
  if (modeset)
     VBE2flip( vidmemsize>>3 );
  else
     _bankedflip2();
}


inline void putp (unsigned int x,unsigned int y, short c)
{   if ( (x < sxsize) && (y < sysize) )
      if (bytesperpixel == 1)
        *((char*)(ruutu)+(x+y*sxsize)) = c;
      else // NOTE! 8/16bits PRESUMED
        *((short*)(ruutu)+(x+y*sxsize)) = c;
}

// **********************************************************
int VBE2_detect()
{   __dpmi_regs r;

    assert(sizeof(vbeinfo) < _go32_info_block.size_of_transfer_buffer);
    strncpy(vbeinfo.VBESig, "VBE2", 4);
    r.x.ax = 0x4F00;
    r.x.di = __tb & 0x0F;
    r.x.es = (__tb >> 4) & 0xFFFF;
    dosmemput(&vbeinfo, sizeof(vbeinfo), __tb);
    __dpmi_int(0x10, &r);
    if (r.h.al != 0x4f) { return(0); }
    dosmemget(__tb, sizeof(vbeinfo), &vbeinfo);
    if (vbeinfo.VBEVer < 0x200) { return(0); }
    return (1);
}

int VBE12_detect()
{   __dpmi_regs r;

    assert(sizeof(vbeinfo) < _go32_info_block.size_of_transfer_buffer);
    strncpy(vbeinfo.VBESig, "VESA", 4);
    r.x.ax = 0x4F00;
    r.x.di = __tb & 0x0F;
    r.x.es = (__tb >> 4) & 0xFFFF;
    dosmemput(&vbeinfo, sizeof(vbeinfo), __tb);
    __dpmi_int(0x10, &r);
    if (r.h.al != 0x4f) { return(0); }
    dosmemget(__tb, sizeof(vbeinfo), &vbeinfo);
    if (vbeinfo.VBEVer < 0x102) { return(0); }
    return (1);
}

//**********************************************************
void getModeInfo(unsigned short mode)
{   __dpmi_regs r;

    assert(sizeof(modeinfo) < _go32_info_block.size_of_transfer_buffer);
    r.x.ax = 0x4F01;
    r.x.cx = mode;
    r.x.di = __tb & 0x0F;
    r.x.es = (__tb >> 4) & 0xFFFF;
    __dpmi_int(0x10, &r);
    dosmemget(__tb, sizeof(modeinfo), &modeinfo);

    banksize = modeinfo.MinWinSize; // use minimum size, JIC
    banksize <<= 10; // in kb's, so mul64
}

void calc_y_adx(int xs, int ys)
{  int i;
   for (i = 0; i < ys; i++)
     scr_y_adxs[i] = i*xs*bytesperpixel;
}


//**********************************************************
int SetVBE2Mode (short mode)
{
    __dpmi_meminfo mi;
    unsigned int linear_address;
    __dpmi_regs r;

    getModeInfo(mode);
    sxsize = modeinfo.XRes;
    sysize = modeinfo.YRes;
    bytesperpixel = (modeinfo.BitsPerPixel+7) >> 3;
    bytexsize = sxsize*bytesperpixel;
    mi.size = (unsigned long)(modeinfo.XRes*modeinfo.YRes*((modeinfo.BitsPerPixel+7) >> 3) );
    mi.address = modeinfo.PhysBasePtr;
    __dpmi_physical_address_mapping(&mi);
    linear_address = mi.address;

    r.x.ax = 0x4F02;
    r.x.bx = 0x4000+mode;
    __dpmi_int(0x10, &r);
    modeset = 1;

//    unsigned char *videoptr = (unsigned char *)0x0;
    vidmemsize = mi.size;
    if (!VBESelector)
      VBESelector = __dpmi_allocate_ldt_descriptors(1);
    __dpmi_set_segment_base_address(VBESelector, linear_address);
    __dpmi_set_segment_limit (VBESelector, mi.size);

    calc_y_adx (sxsize,sysize);

    return (r.h.ah);

//    _farpokeb(VBESelector , y*width +x, color);
}


//**********************************************************
int SetVBE1Mode (short mode)
{   struct VBE_mInfo modeinfo;
    __dpmi_meminfo mi;
    unsigned int linear_address;
    __dpmi_regs r;

    getModeInfo(mode);
    sxsize = modeinfo.XRes;
    sysize = modeinfo.YRes;
    bytesperpixel = (modeinfo.BitsPerPixel+7) >> 3;
    bytexsize = sxsize*bytesperpixel;
    mi.size = (unsigned long)(banksize);
    vidmemsize = mi.size;
//    modeinfo.XRes*modeinfo.YRes);
    mi.address = 0xA0000;
//    modeinfo.PhysBasePtr;
    __dpmi_physical_address_mapping(&mi);
    linear_address = mi.address;

    r.x.ax = 0x4F02;
    r.x.bx = mode;
    __dpmi_int(0x10, &r);
    modeset = 0;

//    unsigned char *videoptr = (unsigned char *)0x0;
/*    VBESelector = __dpmi_allocate_ldt_descriptors(1);
    __dpmi_set_segment_base_address(VBESelector, linear_address);
    __dpmi_set_segment_limit (VBESelector, mi.size); */

    if (!VBESelector)
       VBESelector = __dpmi_allocate_ldt_descriptors(1);
    __dpmi_set_segment_base_address(VBESelector, 0xa0000);
    __dpmi_set_segment_limit (VBESelector, 65536);

    calc_y_adx (sxsize,sysize);

    return (r.h.ah);

//    _farpokeb(VBESelector , y*width +x, color);
}
/*
//-------------------------------------------------------------
void putpic (int x, int y, char *kuva)
{
  int xsize,ysize,yco,xco,xadd;
  int xmaxadd;
  register int offs,n;
  char b;
  xsize = *((int*) (kuva+0) );
  ysize = *((int*) (kuva+4) );

  if (x <= -xsize) return;
//     (x+xsize <= 0)

  n = 8;
  xmaxadd = 0;

  if (y < 0)
    {  if ( (y+ysize) < 0) return;
       n -= y*xsize;
       ysize += y;
       y = 0;
    }

  if (x < 0)
    {  n -= x;
       xmaxadd = -x;
       xsize += x;
       x = 0;
    }

  if (y+ysize >= yclipmax)
    {  ysize = yclipmax-y;  }

  offs = y*sxsize+x;

  if (x+xsize >= xclipmax)
    { xmaxadd = (x+xsize)-xclipmax; xsize = xclipmax-x; }
  xadd = sxsize-xsize;

  for (yco=0; yco<ysize; yco++)
    {  for (xco=0; xco<xsize; xco++)
         {  if ( (b = *(kuva+n++)) != nocolor)
              { *(spritedest+offs) = b; }
            offs++;
         }
       offs += xadd;
       n += xmaxadd;
    }
//  __djgpp_nearptr_disable();
}
*/

//-------------------------------------------------------------
void putcolorpic (int x, int y, void *kuva, register int col)
{
  int xsize,ysize,yco,xco,xadd;
  int xmaxadd;
  register int offs,n;

  xsize = (int) *((int*)(kuva)+0);
  ysize = (int) *((int*)(kuva)+4);

  if (x <= -xsize) return;

  n = 8;
  xmaxadd = 0;

  if (x < 0)
    {  n -= x;
       xmaxadd = -x;
       xsize += x;
       x = 0;
    }

  if (y < 0)
    {  if ( (y+ysize) < 0) return;
       n -= y*xsize;
       ysize += y;
       y = 0;
    }

  if (y+ysize >= sysize)
    {  ysize = sysize-y;  }

  offs = y*sxsize+x;

  if (x+xsize >= sxsize)
    { xmaxadd = (x+xsize)-sxsize; xsize = sxsize-x; }
  xadd = sxsize-xsize;

  if (bytesperpixel == 1)
    {  for (yco=0; yco<ysize; yco++)
         {  for (xco=0; xco<xsize; xco++)
              {  if ( *((char*)(kuva)+n++) != nocolor)
                   { *((char*)(ruutu)+offs) = col; }
                 offs++;
              }
            offs += xadd;
            n += xmaxadd;
         }
    }
  else
    {  for (yco=0; yco<ysize; yco++)
         {  for (xco=0; xco<xsize; xco++)
              {  if ( *((short*)(kuva)+n++) != nocolor)
                   { *((short*)(ruutu)+offs) = col; }
                 offs++;
              }
            offs += xadd;
            n += xmaxadd;
         }
    }

//  __djgpp_nearptr_disable();
}

//-------------------------------------------------------------
void getpic (int x1, int y1, int x2, int y2, void *kuva)
{
  int xsize,ysize,n,yco,xco,xadd,offs;
  xsize = (x2-x1+1);
  ysize = (y2-y1+1);

  *((int*) (kuva)+0 ) = xsize;
  *((int*) (kuva)+1 ) = ysize;

  xadd = sxsize-xsize;
  offs = y1*sxsize+x1;
  n = 4*(bytesperpixel ^ 3);

  for (yco=0; yco<ysize; yco++)
    { for (xco=0; xco<xsize; xco++)
      {  if (bytesperpixel == 1)
            *((char*)(kuva)+n++) = *((char*)(spritedest)+offs++);
          else
            *((short*)(kuva)+n++) = *((short*)(spritedest)+offs++);
      }
      offs = offs + xadd;
    }
}



//-------------------------------------------------------------

void setpal(char col, char r,char g,char b)
{ outportb (0x3C8, col);
  outportb (0x3C9, r);
  outportb (0x3C9, g);
  outportb (0x3C9, b);
}


//-------------------------------------------------------------
// set palette brightness 0..63, whereas 63=max
void setpale (int r, int g, int b)
{ int i;
  if (r > 64) r = 64;
  if (g > 64) g = 64;
  if (b > 64) b = 64;
  if (r < 0) r = 0;
  if (g < 0) g = 0;
  if (b < 0) b = 0;
  outportb (0x3C8, 0);
  for (i=0;i<768;i++)
    { outportb (0x3C9, (int) gen_palette[i++] * r / 64);
      outportb (0x3C9, (int) gen_palette[i++] * g / 64);
      outportb (0x3C9, (int) gen_palette[i] * b / 64);
    }
}

//-------------------------------------------------------------
void waitsync(void)
{  //while( (inportb(0x3DA)&8) != 0);
   while( (inportb(0x3DA)&8) == 0);
}

void abar (int x1, int y1, int x2, int y2)
{ int oadd,offs,x,xb;
  xb = (x2-x1+1);
  oadd = sxsize-(xb);

  offs = y1*sxsize+x1;

  while (y1 <= y2)
    { x = xb;
      while (x > 0)
        { *(spritedest+offs++) = drawcolor;
          x--;
        }
      offs += oadd;
      y1++;
    }
}

//-------------------------------------------------------------
void aline (int x1, int y1, int x2, int y2)
{
  int dxx,dyy;
  int x,y,xi,yi;
  int d2,i1,d,i2,co,bx;

  dxx = (x2-x1); if (dxx < 0) { dxx = -dxx; }
  dyy = (y2-y1); if (dyy < 0) { dyy = -dyy; }

  if (dxx >= dyy)
    { if (x1 > x2)
        { x = x2; y = y2;
          if (y2 > y1) { yi = -1; } else { yi = 1; } }
      else
        { x = x1; y = y1;
          if (y2 > y1) { yi = 1; } else { yi = -1; } }
      d2 = dyy << 1;
      i1 = d2;
      d = d2-dxx;
      i2 = (dyy-dxx) << 1;
      co = dxx+1;
      bx = d;
      while (co > 0)
        {
       //   *(ruutu+(x+y*320)) = drawcolor;
          putp (x,y, drawcolor);
          if (bx < 0)
            { bx = bx+i1; }
          else
            { bx = bx+i2; y = y+yi; }
          co--; x++;
        }
    }
  else
    {
      if (y1 > y2)
        { x = x2; y = y2; if (x2 > x1) { xi = -1; } else { xi = 1; } }
      else
        { x = x1; y = y1; if (x2 > x1) { xi = 1; } else { xi = -1; } }
      d2 = dxx << 1;
      i1 = d2;
      d = d2-dyy;
      i2 = (dxx-dyy) << 1;
      co = dyy+1;
      bx = d;
      while (co > 0)
        {
        //  *(ruutu+(x+y*320)) = drawcolor;
          putp (x,y, drawcolor);
          if (bx < 0)
            { bx = bx+i1; }
          else
            { bx = bx+i2; x = x+xi; }
          co--; y++;
        }
    }
}

//------------------------------------------------------------
void acirclepoint (int x, int y, int xc, int yc)
{  unsigned int xxcp,xxcm,xycp,xycm,yycp,yycm,yxcp,yxcm;
   xxcp = xc+x; xxcm = xc-x;
   xycp = xc+y; xycm = xc-y;
   yxcp = yc+x; yxcm = yc-x;
   yycp = yc+y; yycm = yc-y;
   if ((xxcp < 320) && (yycp < 180))  putp (xxcp,yycp, drawcolor);
   if ((xxcp < 320) && (yycm < 180))  putp (xxcp,yycm, drawcolor);
   if ((xxcm < 320) && (yycp < 180))  putp (xxcm,yycp, drawcolor);
   if ((xxcm < 320) && (yycm < 180))  putp (xxcm,yycm, drawcolor);
   if ((xycp < 320) && (yxcp < 180))  putp (xycp,yxcp, drawcolor);
   if ((xycp < 320) && (yxcm < 180))  putp (xycp,yxcm, drawcolor);
   if ((xycm < 320) && (yxcp < 180))  putp (xycm,yxcp, drawcolor);
   if ((xycm < 320) && (yxcm < 180))  putp (xycm,yxcm, drawcolor);
}

void acircle (int xp,int yp,int radius)
{  int x,y,d;
   x = 0; y = radius; d = 3-(radius+radius);
   while (x < y)
     {  acirclepoint (x,y, xp,yp);
        if (d < 0)
          {  d += (x << 2) + 6;  }
        else
          {  d += ((x-y) << 2) + 10; y--; }
        x++;
     }
   if (x == y)
     acirclepoint (x,y, xp,yp);
}

//-------------------------------------------------------------
void set320 ()
{  if (!VBESelector)
     VBESelector = __dpmi_allocate_ldt_descriptors(1);
   __dpmi_set_segment_base_address(VBESelector, 0xa0000);
   __dpmi_set_segment_limit (VBESelector, 65536);
   textmode (0x13);
   sxsize = 320;
   sysize = 200;
   vidmemsize = 64000;
   bytesperpixel = 1;
   bytexsize = 320;

   modeset = 1; // 320x200 is linear, so use LFB-methods

   calc_y_adx (320,200);

   set_videoparams();
}


//-------------------------------------------
void UnpackLBM (char *name, char *kuva)
{   FILE *tied = fopen ( name, "rb" );
    unsigned int kptr,sptr,pptr,xx,l,count2;
    long fsize;
//    unsigned int *lptr;
    unsigned char *temps;
    unsigned char count,c;

    panic (tied == NULL, name);

    fseek (tied, 0, SEEK_END);
    fsize = ftell(tied);
    fseek (tied, 0, SEEK_SET);
    temps = (char*) malloc(fsize);
    fread (temps, 1, fsize, tied);
    kptr = 0; sptr = 0;

    while (kptr < fsize)
      {
        if ( ( *(temps+kptr+0) == 67) && ( *(temps+kptr+1) == 77) &&
             ( *(temps+kptr+2) == 65) && ( *(temps+kptr+3) == 80) ) // CMAP
          { kptr = kptr + 8;
            for (xx=0;xx<768;xx++)
              { gen_palette[xx] = *(temps+kptr++) / 4; }
          }

        if ( ( *(temps+kptr+0) == 66) && ( *(temps+kptr+1) == 79) &&
             ( *(temps+kptr+2) == 68) && ( *(temps+kptr+3) == 89) ) // BODY
          {
            kptr = kptr + 4;
            l =  ( ( (int) *(temps+kptr+0) ) << 24 ) +
                 ( ( (int) *(temps+kptr+1) ) << 16 ) +
                 ( ( (int) *(temps+kptr+2) ) << 8 )+
                 ( ( (int) *(temps+kptr+3) ) );
            kptr = kptr + 4;
            pptr = 0;
            while (pptr < l)
              { count = *(temps+kptr++); pptr++;
                if (count < 0x80)
                  { for (xx=0;xx<=count;xx++)
                      { *(kuva+sptr++) = *(temps+kptr++); pptr++;
                      }

                  }
                else
                  { count2 = 0x101-(unsigned int)count;
                    c = *(temps+kptr++);
                    for (xx=1;xx<=count2;xx++)
                      { *(kuva+sptr++) = c; }
                    pptr = pptr++;
                  }
              } // end while
          } // end if body
        kptr = kptr + 2;
      } // end while

    fclose (tied);
    free (temps);
}

unsigned char gpix (int x, int y)
{  return (_farpeekb(VBESelector , (y)*320+(x) ) );
}

//*************************************************************
void purakuva (char *name, char* where)
{  FILE *tied = fopen (name, "rb");
   char *temp;
   int size;

   panic (tied == NULL,name);

   fread (&gen_palette, 768,1, tied);
   fread (&size, 4,1, tied);

   temp = (char*) malloc(size);
   fread (temp, size,1, tied);

   sunpackmem (temp, where, size);

   free (temp);

   setpale (64,64,64);
   fclose (tied);
}


//*************************************************************
void waitretrace()
{
   while (! inportb (0x3D8) & 8) ;
}


//*************************************************************
int setvesamode (int mode)
{
  if ((VBE2_detect()) && (!force_bankedVESA))
     {  SetVBE2Mode (mode);
        set_videoparams();
        return (2);
     }
   else
     {
        if (VBE12_detect())
          {  SetVBE1Mode (mode);
             set_videoparams();
             return (1);
          }
        else
          {  printf ("No VESA 1.2 compatible video driver available.\n");
             exit(0);
          }
     }

}


/*
void bankedflip()
{
   int l = 640*480, o = 0, p = 0, s;

   while (l > 0)
     {  if (l > banksize)  s = banksize; else s = l;
        vbe_setbank (p++);

        dosmemput ( ruutu+o, banksize, 0xa0000);

        l -= s;
        o += s;
     }
}
*/

void panic (int i, char *msg)
{  int l;
   if (!i) return;

   textmode (03);
   printf ("FATAL PANIC!!!\n%s\n",msg);
/*   l = strlen (msg);
   for (i = 0; i < l; i+=2)
     printf ("%02x%02x ", *(msg+i), *(msg+i+1) ); */
   printf ("\n");
   printf ("Your computer may now be unstable - please reboot or restart session.\n");
   fflush (NULL);

   exit (0);
}


void set_videoparams()
{  if (bytesperpixel == 1)
     {  putnpic = (void*) putnpic8b;
        putblendpic = (void*) putnpic8b; // no blending available
     }
   else
     {  putnpic = (void*) putnpic16b;
        putblendpic = (void*) putblendpic16b;
     }
   xclipmax = sxsize-1;
   yclipmax = sysize-1;
   /* You may (fairly safely) ignore warnings during compilation
      this routine. REMEMBER THOUGH! incorrect parameters passed
      to putnpic DO cause problems!!! */
}

void arec (int x1, int y1, int x2, int y2)
{  aline (x1,y1, x2,y1);
   aline (x1,y2, x2,y2);
   aline (x1,y1, x1,y2);
   aline (x2,y1, x2,y2);
}

#ifdef DEBUG
void debuglog (char *rivi)
{  FILE *tied;
   tied = fopen ("debug.log","a");

   fprintf (tied, "%s\n", rivi);

   fclose (tied);
}
#endif // DEBUG

